﻿using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using AdTech.Common;

namespace AdTech.Snap.Interop
{
    /// <summary>
    /// Win32 API Helper to listen to and notify about clipboard changes.
    /// </summary>
    internal static class ClipboardHelper
    {
        // Fields
        private static IntPtr targetHwnd;
        private static IntPtr nextHwnd;
        private static Action changedHandler;

        // Methods
        /// <summary>
        /// Starts listening to clipboard changes
        /// </summary>
        /// <param name="hWnd">The target window handle</param>
        /// <param name="handler">The action to be invoked when the clipboard content has changed</param>
        public static void RegisterClipboardHandler(IntPtr hWnd, Action handler)
        {
            targetHwnd = hWnd;
            changedHandler = handler;
            HwndSource.FromHwnd(hWnd).AddHook(new HwndSourceHook(WindowProc));
            nextHwnd = NativeMethods.SetClipboardViewer(targetHwnd);
        }

        /// <summary>
        /// Stops listening to clipboard changes
        /// </summary>
        /// <returns></returns>
        public static bool UnregisterClipboardHandler()
        {
            HwndSource.FromHwnd(targetHwnd).RemoveHook(new HwndSourceHook(WindowProc));
            NativeMethods.ChangeClipboardChain(targetHwnd, nextHwnd);
            return Marshal.GetLastWin32Error() == 0;
        }

        private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch ((WM)msg)
            {
                case WM.CHANGECBCHAIN:
                    nextHwnd = lParam;
                    if (nextHwnd != IntPtr.Zero)
                        NativeMethods.SendMessage(nextHwnd, msg, wParam, lParam);
                    break;

                case WM.DRAWCLIPBOARD:
                    OnClipboardChanged();

                    if (nextHwnd != IntPtr.Zero)
                        NativeMethods.SendMessage(nextHwnd, msg, wParam, lParam);
                    break;
            }
            return IntPtr.Zero;
        }

        private static void OnClipboardChanged()
        {
            if (changedHandler != null)
                changedHandler();
        }
    }
}
